package org.tau.workshop2011.parsing.AST

import java.lang.AssertionError

case class Container(isVertical:Boolean, children:List[Widget], width:Option[DirectExpr], height:Option[DirectExpr])
  extends Widget(if (isVertical) "---" else "|", width, height) {

  for (child <- children)
    child.parent = this

  override def accept(visitor: ASTVisitor): Any = visitor.visit(this)

  override def toString = {
    var in = children map ((wdgt) => "(" + wdgt + ")") mkString ("(", if (isVertical) " --- " else " | ", ")")
    (width, height) match {
      case (None, None) => in
      case _ => "(" + in + ":" +
        (width match { case None => "?"; case Some(w) => "(" + w + ")"}) +
        "x" +
        (height match { case None => "?"; case Some(w) => "(" + w + ")" }) + ")"
    }
  }
}

object Container {

  def Add(sizes: Option[DirectExpr]*) : Option[DirectExpr] = {
    if (sizes.contains(None))
      None
    else
      new Some(Sum(sizes map {(expr) => expr.get} toList, Nil))
  }

  /* Place holder for a more intelligent size comparison */
  def SizesAreSame(size1: DirectExpr, size2: DirectExpr): Boolean = {
    size1 == size2
  }

  def FindUniqueSize(sizes: List[Option[DirectExpr]]): Option[DirectExpr] = {
    val realSizes = sizes filter { (size) => size != None } map { (sSize) => sSize.get }
    if (realSizes == Nil)
      return None

    var result: Option[DirectExpr] = Some(realSizes.head)

    for (size <- realSizes)
      if (! SizesAreSame(size, realSizes.head))
        result = None

    result
  }

  def Contain(isVertical:Boolean, children:List[Widget], width:Option[DirectExpr], height:Option[DirectExpr]) : Widget = {

    if (children.size == 0)
      return new AtomicWidget("", Nil, width, height)

    if (children.size == 1) {
      val underlyingWidget: Widget = children.head

      if (width == None && height == None)
        return underlyingWidget;

      
      val NotNot = { (v1: Option[DirectExpr], v2:Option[DirectExpr]) => if (v1 == None) v2 else v1 }

      /* Check that we don't have two different sizes specified for the same
       * dimension, and if so collect all the sizes on the child widget
       */
      if (((width == None) != (underlyingWidget.getWidth == None))
        && ((height == None) != (underlyingWidget.getHeight == None))) {

        val newWidth = NotNot(underlyingWidget.getWidth, width)
        val newHeight = NotNot(underlyingWidget.getHeight, height)

        return underlyingWidget match {
          case Container(isV, cs, _, _) => Container(isV, cs, newWidth, newHeight)
          case AtomicWidget(kind, attributes, _, _) => AtomicWidget(kind, attributes, newWidth, newHeight)
          case PropertyScope(widget, attributes) => PropertyScope(Contain(false, List(widget), newWidth, newHeight), attributes)
          case _ => throw new AssertionError("Bad widget type!")
        }
      }
    }

    /* If there are two or more children, then we are actually functioning like
     * a container. In that case, attempt to calculate the parent size from the
     * children when unspecified
     */
    if (children.size >= 2) {
      return new Container(isVertical, children,
        /* Width */
        if (width != None)
          width
        else {
          val widths = children map {(child) => child.getWidth }
          if (isVertical)
            FindUniqueSize(widths)
          else
            Add(widths : _*)
        },
        /* Height */
        if (height != None)
          height
        else {
          val heights = children map {(child) => child.getHeight }
          if (! isVertical)
            FindUniqueSize(heights)
          else
            Add(heights : _*)
        }
        )
    }

    /* If reached this part, no optimization works so wrap properly */
    new Container(isVertical, children, width, height)

  }
}